package com.lp.multi.atomic;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author: lp
 * @create: 2021-06-29 15:50
 * @description: UnSafe测试
 */
public class UnSafeTest {
    public static void main(String[] args) {
        //获取Unsafe实例
        Unsafe unsafe = null;
        try {
            // 获取 Unsafe 内部的私有的实例化单例对象
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            // 无视权限
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        //2.分配内存空间
        // 需要传入一个 long 类型的参数，作为申请的内存的大小，单位为 byte
        // 返回这块内存的 long 类型地址
        long memoryAddress=0;
        long newMemoryAddress = 0;
        try{
            memoryAddress = unsafe.allocateMemory(8);

            // 传入之前申请的内存的地址和一个 long 类型的参数作为新的内存的 byte 大小
            // 此方法会释放掉之前地址的内存，然后重新申请一块符合要求大小的内存
            // 如果之前那块内存上已经存在对象了，就会被拷贝到新的内存上
            //newMemoryAddress = unsafe.reallocateMemory(memoryAddress, 32);
            System.out.println("memoryAddress="+memoryAddress);
            System.out.println("newMemoryAddress="+newMemoryAddress);

            //操作内存地址值
            //将 int 型整数 20 存入到指定地址中
            unsafe.putInt(memoryAddress,20);
            //据地址获取到整数
            int value1 = unsafe.getInt(memoryAddress);
            System.out.println("根据内存地址获取到的值为:"+value1);

        }finally {

            if(newMemoryAddress != 0){
                try {
                    unsafe.freeMemory(newMemoryAddress);
                }catch (Exception e){
                    e.printStackTrace();
                    System.out.println("newMemoryAddress释放失败!");
                }
            }

            //传入之前申请的内存的地址就可以释放该块内存了
            //unsafe.reallocateMemory(memoryAddress, 32);重新分配后，再释放memoryAddress内存程序会保存
            if(memoryAddress != 0){
                try {
                    unsafe.freeMemory(memoryAddress);
                }catch (Exception e){
                    e.printStackTrace();
                    System.out.println("memoryAddress释放失败!");
                }
            }
        }



    }
}


class UnSafeTest2 {
    public static void main(String[] args) throws NoSuchFieldException {
        //获取Unsafe实例
        Unsafe unsafe = null;
        try {
            // 获取 Unsafe 内部的私有的实例化单例对象
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            // 无视权限
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        //1 用反射获取变量对象 (getDeclaredField)
        //2 开放权限，屏蔽 private 关键字的影响 (setAccessible(true))
        //3 调用相关方法获取到该对象中的该变量对象的内存地址 (staticFieldOffset/objectFieldOffset)
        //4 通过内存地址去修改该对象的值 (putInt/putObject)
        //5 获取对象的值 (getInt/getObject)


        UnsafeBean bean = new UnsafeBean();

        // 1 测试 staticInt
        // 先通过变量名反射获取到该变量
        Field staticIntField = UnsafeBean.class.getDeclaredField("staticInt");
        //无视权限
        staticIntField.setAccessible(true);
        //staticFieldOffset(...) 方法能够获取到类中的 static 修饰的变量
        long staticIntAddress = unsafe.staticFieldOffset(staticIntField);
        // 使用 put 方法进行值改变，需要传入其所在的 class 对象、内存地址和新的值
        unsafe.putInt(UnsafeBean.class,staticIntAddress,10);
        // 使用 get 方法去获取值，需要传入其所在的 class 对象和内存地址
        int stiatcIntTest = unsafe.getInt(UnsafeBean.class,staticIntAddress);
        // 此处输出为 10
        System.out.println(stiatcIntTest);

        // 2 测试 staticString
        // 基本流程相同，只是 put 和 get 方法换成了 getObject(...) 和 putObject(...)
        Field staticStringField = UnsafeBean.class.getDeclaredField("staticString");
        staticStringField.setAccessible(true);
        long staticStringAddress = unsafe.staticFieldOffset(staticStringField);
        unsafe.putObject(UnsafeBean.class,staticStringAddress,"static_string_2");
        String staticStringTest = (String)unsafe.getObject(UnsafeBean.class,staticStringAddress);
        /// 此处输出为 static_string_2
        System.out.println(staticStringTest);

        // 3 测试 finalInt
        // 基本流程相同，只是 staticFieldOffset(...) 方法换成了 objectFieldOffset(...) 方法
        Field finalIntField = UnsafeBean.class.getDeclaredField("finalInt");
        finalIntField.setAccessible(true);
        long finalIntAddress = unsafe.objectFieldOffset(finalIntField);
        // 需要注意的是，虽然该变量是 final 修饰的，理论上是不可变的变量，但是 unsafe 是具有修改权限的
        unsafe.putInt(bean,finalIntAddress,10);
        int finalIntTest = unsafe.getInt(bean,finalIntAddress);
        // 此处输出为 10
        System.out.println(finalIntTest);

        // 4 测试 finalString
        Field finalStringField = UnsafeBean.class.getDeclaredField("finalString");
        finalStringField.setAccessible(true);
        long finalStringAddress = unsafe.objectFieldOffset(finalStringField);
        unsafe.putObject(bean,finalStringAddress,"final_string_2");
        String finalStringTest = (String)unsafe.getObject(bean,finalStringAddress);
        /// 此处输出为 final_string_2
        System.out.println(finalStringTest);

        // TODO 如果获取普通变量赋值的地址：String str="hello unsafe";

        //CAS
        // 创建一个 Integer 对象，value 为 1
        Integer i = 1;

        // 获取到内部变量 value，这个变量用于存放值
        Field valueField = Integer.class.getDeclaredField("value");
        valueField.setAccessible(true);

        // 获取到内存偏移量值
        long valueAddress = unsafe.objectFieldOffset(valueField);

        // getIntVolatile 使用偏移量获取到对象内指定变量的值
        int originValue_1 = unsafe.getIntVolatile(i,valueAddress);
        // getIntVolatile 获取 int 的值
        // getDoubleVolatile 获取 double 的值
        // getLongVolatile 获取 long 的值
        // getObjectVolatile 获取 object 的值

        // getAndAddInt 方法和 getIntVolatile 一样会获取到变量的值，同时会加上一个值
        int originValue_2 = unsafe.getAndAddInt(i,valueAddress,5);
        // getAndAddInt 获取值并加上一个 int 类型的整数
        // getAndAddLong 获取值并加上一个 long 类型的整数

        // getAndAddInt 方法和 getIntVolatile 一样会获取到变量的值，同时会替换一个值
        int originValue_3 = unsafe.getAndSetInt(i,valueAddress,5);

        // 该方法用户比较及替换值
        // 第一个参数为要替换的对象本身，第二个参数为值的内存地址
        // 第三个参数为变量的预期值，第四个参数为变量要换的值
        // 如果变量目前的值等于预期值(第三个参数)，则会将变量的值换成新值(第四个参数)，返回 true
        // 如果不等于预期，则不会改变，并返回 false
        boolean isOk = unsafe.compareAndSwapInt(i,valueAddress,1,5);



        //此处输出 true
        System.out.println(isOk);
        //此处输出 5
        System.out.println(i);

    }
}


//测试实体
class UnsafeBean{
    // 测试1 测试 static 修饰的 int 类型的存取
    private static int staticInt = 5;
    // 测试2 测试 static 修饰的 object 类型的存取
    private static String staticString = "static_string";
    // 测试3 测试 final 修饰的 int 类型的存取
    private final int finalInt = 5;
    // 测试4 测试 final 修饰的 object 类型的存取
    private final String finalString = "final_string";
    // 测试5 测试一般的 int 类型的存取
    private int privateInt;
    // 测试6 测试一般的 object 类型的存取
    private String privateString;

    public static int getStaticInt() {
        return staticInt;
    }

    public static void setStaticInt(int staticInt) {
        UnsafeBean.staticInt = staticInt;
    }

    public static String getStaticString() {
        return staticString;
    }

    public static void setStaticString(String staticString) {
        UnsafeBean.staticString = staticString;
    }

    public int getFinalInt() {
        return finalInt;
    }

    public String getFinalString() {
        return finalString;
    }

    public int getPrivateInt() {
        return privateInt;
    }

    public void setPrivateInt(int privateInt) {
        this.privateInt = privateInt;
    }

    public String getPrivateString() {
        return privateString;
    }

    public void setPrivateString(String privateString) {
        this.privateString = privateString;
    }
}

